package com.luzhikun.java;

import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author shkstart
 * @create 2021-02-23 15:19
 */
/*
 * Java反射机制提供的功能
 * 在一运行时判断任意一个对象所属的类
 * 在运行时构造任意一个类的对象
 * 在运行时判断任意个类所具有的成员变量和方法
 * 在运行时获取泛型信息
 * 在运行时调用任意一个对象的成员变量和方法
 * 在运行时处理注解
 * 生成动态代理
 *
 * */
public class RefluctionTest {
    public static void main(String[] args) {
        //1.造Person的对象
        Person person = new Person();
        Person person1 = new Person("小明", 12);
//        Person person2=new Person("小花");
        //2。调用方法 和属性
        person.show();
        person.toString();
        person.age = 12;

        //在Person类的外部，不可以通过Person累的对象调用其内部私有结构
        //比如:name/showNAtion以及私有的构造器

    }

    //反射之后对于Person的操作
    @Test
    public void test1() throws Exception {
        Class clazz = Person.class;
        //通过反射，创建Person类的对象
        Constructor con = clazz.getConstructor(String.class, int.class);
        Object obj = con.newInstance("小明", 12);
        Person pe1 = (Person) obj;
        System.out.println(obj);
        System.out.println(pe1);
        //通过反射，调用对象指定的方法和属性
//        pe1.show();
//        pe1.age=13;
//        System.out.println(pe1);
        //调属性
        Field age1 = clazz.getDeclaredField("age");
        age1.set(obj, 15);
        System.out.println(obj);
        //调方法
        Method m1 = clazz.getDeclaredMethod("show");
        m1.invoke(obj);
        System.out.println("-------------------------------------");

        //通过反射可以调用Person中私有的结构
        //调用私有的构造器
        Constructor dcr = clazz.getDeclaredConstructor(String.class);
        dcr.setAccessible(true);
        Person person = (Person) dcr.newInstance("小花");
        System.out.println(person.toString());
        //调用私有的方法
        Method showNation = clazz.getDeclaredMethod("showNation");
        showNation.setAccessible(true);
        showNation.invoke(person);
        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(person, "小李");
        System.out.println(person);
    }
    //疑问：通过直接new对象 或反射方式 都可以调用公共结构，开发中用哪个
    //建议 还是 直接new的方式
    //什么时候会使用：反射的方式 反射的特征： 动态性
    //如果我们在new 对象的时候 还不能确定 到底要new 那个类的对象 就用反射的方式
    //疑问：反射机制与面向对象中的封装性是否矛盾？如何看待这两项技术
    //不矛盾
    //封装性体现的是 建议你去调什么 公共的建议你用 私有的 不建议用
    //反射的方式 解决的是 能不能调的问题，你可以调 但是 私有的很多没必要调用

    /*
     *
     * 关于java.lang.Class类的理解
     * 1.类的加载过程：
     * 程序在经过javac.exe命令后 会生成一个或多个字节码文件(.class结尾)，
     * 接着我们使用java.exe命令对某个字节码文件进行解释运行，相当于将某个字节码文件加载到内存中，此过程就称为类的加载，加载到内存的类，我们就称为
     * 运行时类，此运行时类就作为Class的一个实例
     *
     * 2.换句话说 Class类的实例就对应着一个运行时类
     *
     *3.加载到内存中的运行时类，会缓存一定的时间，在此时间之内，我们可以通过不同的方式来获取此运行时类
     * */
    //获取Class类的实例的方式(前三种方式需要掌握)
    @Test
    public void test2() throws ClassNotFoundException {
        //方式一：调用运行时类的属性：，class
        Class<Person> clazz = Person.class;
        System.out.println(clazz);
        //方式二：通过运行时类的对象
        Person p1 = new Person();
        Class claz2 = p1.getClass();
        System.out.println(claz2);

        //方式三：调用Class的静态方法：forName（String classPath）（用的多）
        Class<?> aClass = Class.forName("com.luzhikun.java.Person");
        System.out.println(aClass);

        //方式四：使用类的加载器ClassLoder(了解)
        ClassLoader l1 = RefluctionTest.class.getClassLoader();
        Class<?> aClass1 = l1.loadClass("com.luzhikun.java.Person");
        System.out.println(aClass1);

        System.out.println(claz2 = clazz);
        System.out.println(aClass = clazz);
        System.out.println(claz2 = aClass1);

    }

    //Class实例可以是那些结构的说明
    @Test
    public void test3() {

        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class<String> stringClass = String.class;
        Class<Integer> integerClass = int.class;
        Class<int[][]> aClass = int[][].class;
        Class<ElementType> elementTypeClass = ElementType.class;
        Class<Override> overrideClass = Override.class;
        Class<Void> voidClass = void.class;
        Class<Class> classClass = Class.class;

        int[] a = new int[1];
        int[] b = new int[12];
        Class c3 = a.getClass();
        Class c4 = b.getClass();
        System.out.println(c3 == c4);


    }
}